<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <meta name="author" content="Mouse0w0">
  
  <link rel="shortcut icon" href="../img/favicon.ico">
  <title>纹理 - Lwjglbook中文翻译</title>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700" />

  <link rel="stylesheet" href="../css/theme.css" />
  <link rel="stylesheet" href="../css/theme_extra.css" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" />
  
  <script>
    // Current page data
    var mkdocs_page_name = "\u7eb9\u7406";
    var mkdocs_page_input_path = "07-textures.md";
    var mkdocs_page_url = null;
  </script>
  
  <script src="../js/jquery-2.1.1.min.js" defer></script>
  <script src="../js/modernizr-2.8.3.min.js" defer></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
  <script>hljs.initHighlightingOnLoad();</script> 
  
</head>

<body class="wy-body-for-nav" role="document">

  <div class="wy-grid-for-nav">

    
    <nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
    <div class="wy-side-scroll">
      <div class="wy-side-nav-search">
        <a href=".." class="icon icon-home"> Lwjglbook中文翻译</a>
        <div role="search">
  <form id ="rtd-search-form" class="wy-form" action="../search.html" method="get">
    <input type="text" name="q" placeholder="Search docs" title="Type search term here" />
  </form>
</div>
      </div>

      <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../01-first-steps/">事前准备</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../02-the-game-loop/">游戏循环</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../03-a-brief-about-coordinates/">坐标简介</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../04-rendering/">渲染</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../05-more-on-rendering/">渲染补充</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../06-transformations/">变换</a>
                    </li>
                </ul>
                <ul class="current">
                    <li class="toctree-l1 current"><a class="reference internal current" href="./">纹理</a>
    <ul class="current">
    <li class="toctree-l2"><a class="reference internal" href="#_1">创建一个三维立方体</a>
    </li>
    <li class="toctree-l2"><a class="reference internal" href="#_2">为立方体添加纹理</a>
    </li>
    <li class="toctree-l2"><a class="reference internal" href="#_3">透明纹理简介</a>
    </li>
    </ul>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../08-camera/">摄像机</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../09-loading-more-complex-models/">加载更复杂的模型</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../10-let-there-be-light/">要有光</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../11-let-there-be-even-more-light/">要有更多的光</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../12-game-hud/">游戏HUD</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../13-sky-box-and-some-optimizations/">天空盒与一些优化</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../14-height-maps/">高度图</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../15-terrain-collisions/">地形碰撞</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../16-fog/">雾</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../17-normal-mapping/">法线贴图</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../18-shadows/">阴影</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../19-animations/">动画</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../20-particles/">粒子</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../21-instanced-rendering/">实例化渲染</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../22-audio/">音效</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../23-3d-object-picking/">三维物体选取</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../24-hud-revisited/">回顾HUD - NanoVG</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../25-optimizations-frustum-culling/">优化 - 截锥剔除</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../26-cascaded-shadow-maps/">级联阴影映射</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../27-assimp/">Assimp库</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../28-deferred-shading/">延迟着色法</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../a01-opengl-debugging/">附录 A - OpenGL调试</a>
                    </li>
                </ul>
                <ul>
                    <li class="toctree-l1"><a class="reference internal" href="../glossary/">术语表</a>
                    </li>
                </ul>
      </div>
    </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">

      
      <nav class="wy-nav-top" role="navigation" aria-label="top navigation">
        <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
        <a href="..">Lwjglbook中文翻译</a>
      </nav>

      
      <div class="wy-nav-content">
        <div class="rst-content">
          <div role="navigation" aria-label="breadcrumbs navigation">
  <ul class="wy-breadcrumbs">
    <li><a href="..">Docs</a> &raquo;</li>
    
      
    
    <li>纹理</li>
    <li class="wy-breadcrumbs-aside">
      
        <a href="https://github.com/Mouse0w0/lwjglbook-CN-Translation/edit/master/docs/07-textures.md"
          class="icon icon-github"> Edit on GitHub</a>
      
    </li>
  </ul>
  
  <hr/>
</div>
          <div role="main">
            <div class="section">
              
                <h1 id="textures">纹理（Textures）</h1>
<h2 id="_1">创建一个三维立方体</h2>
<p>在本章中，我们将学习如何在渲染中加载纹理并使用它们。为了讲解与纹理相关的所有概念，我们将把此前章节中使用的正方形更改为三维立方体。为了绘制一个立方体，我们只需要正确地定义一个立方体的坐标，就能使用现有代码正确地绘制它。</p>
<p>为了绘制立方体，我们只需要定义八个顶点。</p>
<p><img alt="立方体坐标" src="../_static/07/cube_coords.png" /></p>
<p>因此，它的坐标数组将是这样的：</p>
<pre><code class="java">float[] positions = new float[] {
    // VO
    -0.5f,  0.5f,  0.5f,
    // V1
    -0.5f, -0.5f,  0.5f,
    // V2
    0.5f, -0.5f,  0.5f,
    // V3
     0.5f,  0.5f,  0.5f,
    // V4
    -0.5f,  0.5f, -0.5f,
    // V5
     0.5f,  0.5f, -0.5f,
    // V6
    -0.5f, -0.5f, -0.5f,
    // V7
     0.5f, -0.5f, -0.5f,
};
</code></pre>

<p>当然，由于我们多了4个顶点，我们需要更改颜色数组，目前仅重复前四项的值。</p>
<pre><code class="java">float[] colours = new float[]{
    0.5f, 0.0f, 0.0f,
    0.0f, 0.5f, 0.0f,
    0.0f, 0.0f, 0.5f,
    0.0f, 0.5f, 0.5f,
    0.5f, 0.0f, 0.0f,
    0.0f, 0.5f, 0.0f,
    0.0f, 0.0f, 0.5f,
    0.0f, 0.5f, 0.5f,
};
</code></pre>

<p>最后，由于立方体是由六个面构成的，需要绘制十二个三角形（每个面两个），因此我们需要修改索引数组。记住三角形必须按逆时针顺序定义，如果你直接去定义三角形，很容易犯错。一定要将你想定义的面摆在你的面前，确认顶点并以逆时针顺序绘制三角形。</p>
<pre><code class="java">int[] indices = new int[] {
    // 前面
    0, 1, 3, 3, 1, 2,
    // 上面
    4, 0, 3, 5, 4, 3,
    // 右面
    3, 2, 7, 5, 3, 7,
    // 左面
    6, 1, 0, 6, 0, 4,
    // 下面
    2, 1, 6, 2, 6, 7,
    // 后面
    7, 6, 4, 7, 4, 5,
};
</code></pre>

<p>为了更好观察立方体，我们将修改<code>DummyGame</code>类中旋转模型的代码，使模型沿着三个轴旋转。</p>
<pre><code class="java">// 更新旋转角
float rotation = gameItem.getRotation().x + 1.5f;
if ( rotation &gt; 360 ) {
    rotation = 0;
}
gameItem.setRotation(rotation, rotation, rotation);
</code></pre>

<p>这就完了，现在能够显示一个旋转的三维立方体了，你可以编译和运行示例代码，会得到如下所示的东西。</p>
<p><img alt="没有开启深度测试的立方体" src="../_static/07/cube_no_depth_test.png" /></p>
<p>这个立方体有些奇怪，有些面没被正确地绘制，这发生了什么？立方体之所以出现这个现象，是因为组成立方体的三角形是以一种随机顺序绘制的。事实上距离较远的像素应该在距离较近的像素之前绘制，而不是现在这样。为了修复它，我们必须启用深度测试（Depth Test）。</p>
<p>这将在<code>Window</code>类的<code>init</code>方法中去做：</p>
<pre><code class="java">glEnable(GL_DEPTH_TEST);
</code></pre>

<p>现在立方体被正确地渲染了！</p>
<p><img alt="开启深度测试的立方体" src="../_static/07/cube_depth_test.png" /></p>
<p>如果你看了本章该小节的代码，你可能会看到<code>Mesh</code>类做了一下小规模的调整，VBO的ID现在被储存在一个List中，以便于迭代它们。</p>
<h2 id="_2">为立方体添加纹理</h2>
<p>现在我们将把纹理应用到立方体上。纹理（Texture）是用来绘制某个模型的像素颜色的图像，可以认为纹理是包在三维模型上的皮肤。你要做的是将纹理图像中的点分配给模型中的顶点，这样做OpenGL就能根据纹理图像计算其他像素的颜色。</p>
<p><img alt="纹理映射" src="../_static/07/texture_mapping.png" /></p>
<p>纹理图像不必与模型同样大小，它可以变大或变小。如果要处理的像素不能映射到纹理中的指定点，OpenGL将推断颜色。可在创建纹理时控制如何进行颜色推断。</p>
<p>因此，为了将纹理应用到模型上，我们必须做的是将纹理坐标分配给每个顶点。纹理坐标系有些不同于模型坐标系。首先，我们的纹理是二维纹理，所以坐标只有X和Y两个量。此外，原点是图像的左上角，X或Y的最大值都是1。</p>
<p><img alt="纹理坐标系" src="../_static/07/texture_coordinates.png" /></p>
<p>我们如何将纹理坐标与位置坐标联系起来呢？答案很简单，就像传递颜色信息，我们创建了一个VBO，为每个顶点储存其纹理坐标。</p>
<p>让我们开始修改代码，以便在三维立方体上使用纹理吧。首先是加载将被用作纹理的图像。对此在LWJGL的早期版本中，通常使用Slick2D库。在撰写本文时，该库似乎与LWJGL 3不兼容，因此我们需要使用另一种方法。我们将使用LWJGL为<a href="https://github.com/nothings/stb">stb</a>库提供的封装。为了使用它，首先需要在本地的<code>pom.xml</code>文件中声明依赖。</p>
<pre><code class="xml">&lt;dependency&gt;
    &lt;groupId&gt;org.lwjgl&lt;/groupId&gt;
    &lt;artifactId&gt;lwjgl-stb&lt;/artifactId&gt;
    &lt;version&gt;${lwjgl.version}&lt;/version&gt;
&lt;/dependency&gt;
[...]
&lt;dependency&gt;
    &lt;groupId&gt;org.lwjgl&lt;/groupId&gt;
    &lt;artifactId&gt;lwjgl-stb&lt;/artifactId&gt;
    &lt;version&gt;${lwjgl.version}&lt;/version&gt;
    &lt;classifier&gt;${native.target}&lt;/classifier&gt;
    &lt;scope&gt;runtime&lt;/scope&gt;
&lt;/dependency&gt;
</code></pre>

<p>在一些教程中，你可能看到首先要做的事是调用<code>glEnable(GL_TEXTURE_2D)</code>来启用OpenGL环境中的纹理。如果使用固定管线这是对的，但我们使用GLSL着色器，因此不再需要了。</p>
<p>现在我们将创建一个新的<code>Texture</code>类，它将执行加载纹理所必须的步骤。首先，我们需要将图像载入到<code>ByteBuffer</code>中，代码如下：</p>
<pre><code class="java">private static int loadTexture(String fileName) throws Exception {
    int width;
    int height;
    ByteBuffer buf;
    // 加载纹理文件
    try (MemoryStack stack = MemoryStack.stackPush()) {
        IntBuffer w = stack.mallocInt(1);
        IntBuffer h = stack.mallocInt(1);
        IntBuffer channels = stack.mallocInt(1);

        buf = stbi_load(fileName, w, h, channels, 4);
        if (buf == null) {
            throw new Exception(&quot;Image file [&quot; + fileName  + &quot;] not loaded: &quot; + stbi_failure_reason());
        }

        /* 获得图像的高度与宽度 */
        width = w.get();
        height = h.get();
     }
     [... 接下来还有更多代码 ...]
</code></pre>

<p>首先我们要为库分配<code>IntBuffer</code>，以返回图像大小与通道数。然后，我们调用<code>stbi_load</code>方法将图像加载到<code>ByteBuffer</code>中，该方法需要如下参数：</p>
<ul>
<li><code>filePath</code>：文件的绝对路径。stb库是本地的，不知道关于<code>CLASSPATH</code>的任何内容。因此，我们将使用常规的文件系统路径。</li>
<li><code>width</code>：图像宽度，获取的图像宽度将被写入其中。</li>
<li><code>height</code>：图像高度，获取的图像高度将被写入其中。</li>
<li><code>channels</code>：图像通道。</li>
<li><code>desired_channels</code>：所需的图像通道，我们传入4（RGBA）。</li>
</ul>
<p>一件关于OpenGL的重要事项，由于历史原因，要求纹理图像的大小（每个轴的像素数）必须是二的指数（2, 4, 8, 16, ....）。一些驱动解除了这种限制，但最好还是保持以免出现问题。</p>
<p>下一步是将纹理上传到显存中。首先需要创建一个新的纹理ID，与该纹理相关的操作都要使用该ID，因此我们需要绑定它。</p>
<pre><code class="java">// 创建一个新的OpenGL纹理
int textureId = glGenTextures();
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, textureId);
</code></pre>

<p>然后需要告诉OpenGL如何解包RGBA字节，由于每个分量只有一个字节大小，所以我们需要添加以下代码：</p>
<pre><code class="java">glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
</code></pre>

<p>最后我们可以上传纹理数据：</p>
<pre><code class="java">glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
    0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
</code></pre>

<p><code>glTexImage2D</code>的参数如下所示：</p>
<ul>
<li><code>target</code>: 指定目标纹理（纹理类型），本例中是<code>GL_TEXTURE_2D</code>。</li>
<li><code>level</code>: 指定纹理细节的等级。0级是基本图像等级，第n级是第n个多级渐远纹理的图像，之后再谈论这个问题。</li>
<li><code>internal format</code>: 指定纹理中颜色分量的数量。</li>
<li><code>width</code>: 指定纹理图像的宽度。</li>
<li><code>height</code>: 指定纹理图像的高度。</li>
<li><code>border</code>: 此值必须为0。</li>
<li><code>format</code>: 指定像素数据的格式，现在为RGBA。</li>
<li><code>type</code>: 指定像素数据的类型。现在，我们使用的是无符号字节。</li>
<li><code>data</code>: 储存数据的缓冲区。</li>
</ul>
<p>在一些代码中，你可能会发现在调用<code>glTexImage2D</code>方法前设置了一些过滤参数。过滤是指在缩放时如何绘制图像，以及如何插值像素。如果未设置这些参数，纹理将不会显示。因此，在<code>glTexImage2D</code>方法调用之前，会看到以下代码：</p>
<pre><code class="java">glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
</code></pre>

<p>这些参数基本上在说，当绘制一个像素时，如果没有直接一对一地关联到纹理坐标，它将选择最近的纹理坐标点。</p>
<p>到目前为止，我们不会设置这些参数。相反，我们将生成一个多级渐远纹理（Mipmap）。多级渐远纹理是由高细节纹理生成的逐级降低分辨率的纹理集合。当我们的物体缩放时，就将自动使用低分辨率的图像。</p>
<p>为了生成多级渐远纹理，只需要编写以下代码（目前我们把它放在<code>glTextImage2D</code>方法调用之后）：</p>
<pre><code class="java">glGenerateMipmap(GL_TEXTURE_2D);
</code></pre>

<p>最后，我们可以释放原始图像数据本身的内存：</p>
<pre><code class="java">stbi_image_free(buf);
</code></pre>

<p>就这样，我们已经成功地加载了纹理，现在需要使用它。正如此前所说，我们需要把纹理坐标作为另一个VBO。因此，我们要修改<code>Mesh</code>类以接收储存纹理坐标的浮点数组，而不是颜色（我们可以同时有颜色和纹理，但为了简化它，我们将删除颜色），构造函数现在如下所示：</p>
<pre><code class="java">public Mesh(float[] positions, float[] textCoords, int[] indices,
    Texture texture)
</code></pre>

<p>纹理坐标VBO与颜色VBO创建的方式相同。唯一的区别是它每个顶点属性只有两个分量而不是三个：</p>
<pre><code class="java">vboId = glGenBuffers();
vboIdList.add(vboId);
textCoordsBuffer = MemoryUtil.memAllocFloat(textCoords.length);
textCoordsBuffer.put(textCoords).flip();
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, textCoordsBuffer, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
</code></pre>

<p>现在我们需要在着色器中使用纹理。在顶点着色器中，我们修改了第二个输入参数，因为现在它是一个<code>vec2</code>（也顺便更改了名称）。顶点着色器就像此前一样，仅将纹理坐标传给片元着色器。</p>
<pre><code class="glsl">#version 330

layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord;

out vec2 outTexCoord;

uniform mat4 worldMatrix;
uniform mat4 projectionMatrix;

void main()
{
    gl_Position = projectionMatrix * worldMatrix * vec4(position, 1.0);
    outTexCoord = texCoord;
}
</code></pre>

<p>在片元着色器中，我们使用那些纹理坐标来设置像素颜色：</p>
<pre><code class="glsl">#version 330

in  vec2 outTexCoord;
out vec4 fragColor;

uniform sampler2D texture_sampler;

void main()
{
    fragColor = texture(texture_sampler, outTexCoord);
}
</code></pre>

<p>在分析代码之前，我们先理清一些概念。显卡有几个空间或槽来储存纹理，每一个空间被称为纹理单元（Texture Unit）。当使用纹理时，我们必须设置想用的纹理。如你所见，我们有一个名为<code>texture_sampler</code>的新Uniform，该Uniform的类型是<code>sampler2D</code>，并储存有我们希望使用的纹理单元的值。</p>
<p>在<code>main</code>函数中，我们使用名为<code>texture</code>的纹理采样函数，该函数有两个参数：取样器（Sampler）和纹理坐标，并返回正确的颜色。取样器Uniform允许使用多重纹理（Multi-texture），不过现在不是讨论这个话题的时候，但是我们会在稍后再尝试添加。</p>
<p>因此，在<code>ShaderProgram</code>类中，我们将创建一个新的方法，允许为整数型Uniform设置值：</p>
<pre><code class="java">public void setUniform(String uniformName, int value) {
    glUniform1i(uniforms.get(uniformName), value);
}
</code></pre>

<p>在<code>Renderer</code>类的<code>init</code>方法中，我们将创建一个新的Uniform：</p>
<pre><code class="java">shaderProgram.createUniform(&quot;texture_sampler&quot;);
</code></pre>

<p>此外，在<code>Renderer</code>类的<code>render</code>方法中，我们将Uniform的值设置为0（我们现在不使用多个纹理，所以只使用单元0）。</p>
<pre><code class="java">shaderProgram.setUniform(&quot;texture_sampler&quot;, 0);
</code></pre>

<p>最好，我们只需修改<code>Mesh</code>类的<code>render</code>方法就可以使用纹理。在方法起始处，添加以下几行代码：</p>
<pre><code class="java">// 激活第一个纹理单元
glActiveTexture(GL_TEXTURE0);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, texture.getId());
</code></pre>

<p>我们已经将<code>texture.getId()</code>所获得的纹理ID绑定到纹理单元0上。</p>
<p>我们刚刚修改了代码来支持纹理，现在需要为三维立方体设置纹理坐标，纹理图像文件是这样的：</p>
<p><img alt="立方体纹理" src="../_static/07/cube_texture.png" /></p>
<p>在我们的三维模型中，共有八个顶点。我们首先定义正面每个顶点的纹理坐标。</p>
<p><img alt="立方体纹理的正面" src="../_static/07/cube_texture_front_face.png" /></p>
<table>
<thead>
<tr>
<th>顶点</th>
<th>纹理坐标</th>
</tr>
</thead>
<tbody>
<tr>
<td>V0</td>
<td>
<script type="math/tex">0.0, 0.0</script>
</td>
</tr>
<tr>
<td>V1</td>
<td>
<script type="math/tex">0.0, 0.5</script>
</td>
</tr>
<tr>
<td>V2</td>
<td>
<script type="math/tex">0.5, 0.5</script>
</td>
</tr>
<tr>
<td>V3</td>
<td>
<script type="math/tex">0.5, 0.0</script>
</td>
</tr>
</tbody>
</table>
<p>然后，定义顶面的纹理映射。</p>
<p><img alt="正方体纹理的顶面" src="../_static/07/cube_texture_top_face.png" /></p>
<table>
<thead>
<tr>
<th>顶点</th>
<th>纹理坐标</th>
</tr>
</thead>
<tbody>
<tr>
<td>V4</td>
<td>
<script type="math/tex">0.0, 0.5</script>
</td>
</tr>
<tr>
<td>V5</td>
<td>
<script type="math/tex">0.5, 0.5</script>
</td>
</tr>
<tr>
<td>V0</td>
<td>
<script type="math/tex">0.0, 1.0</script>
</td>
</tr>
<tr>
<td>V3</td>
<td>
<script type="math/tex">0.5, 1.0</script>
</td>
</tr>
</tbody>
</table>
<p>如你所见，有一个问题，我们需要为同一个顶点（V0和V3）设置不同的纹理坐标。怎么样才能解决这个问题呢？解决这一问题的唯一方法是重复一些顶点并关联不同的纹理坐标。对于顶面，我们需要重复四个顶点并为它们分配正确的纹理坐标。</p>
<p>因为前面、后面和侧面都使用相同的纹理，所以我们不需要重复这些顶点。在源码中有完整的定义，但是我们需要从8个点上升到20个点了。最终的结果就像这样。</p>
<p><img alt="有纹理的立方体" src="../_static/07/cube_with_texture.png" /></p>
<p>在接下来的章节中，我们将学习如何加载由3D建模工具生成的模型，这样我们就不需要手动定义顶点和纹理坐标了（顺便一提，对于更复杂的模型，手动定义是不存在的）。</p>
<h2 id="_3">透明纹理简介</h2>
<p>如你所见，当加载图像时，我们检索了四个RGBA组件，包括透明度等级。但如果加载一个透明的纹理，可能看不到任何东西。为了支持透明度，我们需要通过以下代码启用混合（Blend）：</p>
<pre><code class="java">glEnable(GL_BLEND);
</code></pre>

<p>但仅启用混合，透明效果仍然不会显示，我们还需要指示OpenGL如何进行混合。这是通过调用<code>glBlendFunc</code>方法完成的：</p>
<pre><code class="java">glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
</code></pre>

<p>你可以查看<a href="https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/03%20Blending/">此处</a>有关可使用的不同功能的详细说明。</p>
<p>即使启用了混合并设置了功能，也可能看不到正确的透明效果。其原因是深度测试，当使用深度值丢弃片元时，我们可能将具有透明度的片元与背景混合，而不是与它们后面的片元混合，这将得到错误的渲染结果。为了解决该问题，我们需要先绘制不透明物体，然后按深度递减顺序绘制具有透明度的物体（应先绘制较远物体）。</p>
              
            </div>
          </div>
          <footer>
  
    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
      
        <a href="../08-camera/" class="btn btn-neutral float-right" title="摄像机">Next <span class="icon icon-circle-arrow-right"></span></a>
      
      
        <a href="../06-transformations/" class="btn btn-neutral" title="变换"><span class="icon icon-circle-arrow-left"></span> Previous</a>
      
    </div>
  

  <hr/>

  <div role="contentinfo">
    <!-- Copyright etc -->
    
      <p>2019, Mouse0w0</p>
    
  </div>

  Built with <a href="https://www.mkdocs.org/">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
      
        </div>
      </div>

    </section>

  </div>

  <div class="rst-versions" role="note" aria-label="versions">
    <span class="rst-current-version" data-toggle="rst-current-version">
      
          <a href="https://github.com/Mouse0w0/lwjglbook-CN-Translation/" class="fa fa-github" style="float: left; color: #fcfcfc"> GitHub</a>
      
      
        <span><a href="../06-transformations/" style="color: #fcfcfc;">&laquo; Previous</a></span>
      
      
        <span style="margin-left: 15px"><a href="../08-camera/" style="color: #fcfcfc">Next &raquo;</a></span>
      
    </span>
</div>
    <script>var base_url = '..';</script>
    <script src="../js/theme.js" defer></script>
      <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" defer></script>
      <script src="../search/main.js" defer></script>
    <script defer>
        window.onload = function () {
            SphinxRtdTheme.Navigation.enable(true);
        };
    </script>

</body>
</html>
